package com.joysuch.wwyt.edu.schedule;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.zlg.common.util.CollectionUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Sets;
import com.joysuch.wwyt.bp.entity.BpDepart;
import com.joysuch.wwyt.bp.entity.contractor.BpContractorStaff;
import com.joysuch.wwyt.bp.repository.BpContractorStaffDao;
import com.joysuch.wwyt.bp.repository.BpDepartDao;
import com.joysuch.wwyt.common.entity.CommonBusinessNotice;
import com.joysuch.wwyt.common.entity.CommonIndexNotify;
import com.joysuch.wwyt.common.enums.AppBusinessNoticeTypes;
import com.joysuch.wwyt.common.enums.CommonIndexNotifyTypes;
import com.joysuch.wwyt.common.repository.CommonIndexNotifyDao;
import com.joysuch.wwyt.common.service.CommonBusinessNoticeService;
import com.joysuch.wwyt.core.entity.BaseManageType;
import com.joysuch.wwyt.core.entity.BaseUser;
import com.joysuch.wwyt.core.repository.BaseUserDao;
import com.joysuch.wwyt.core.service.BaseManageTypeService;
import com.joysuch.wwyt.edu.entity.EduCertificateNotify;
import com.joysuch.wwyt.edu.entity.WwytEduCertificate;
import com.joysuch.wwyt.edu.entity.WwytEduCertificateUser;
import com.joysuch.wwyt.edu.enums.CertStatus;
import com.joysuch.wwyt.edu.enums.CertificateNotifyObjType;
import com.joysuch.wwyt.edu.enums.CertificateNotifyStatus;
import com.joysuch.wwyt.edu.enums.CertificateNotifyType;
import com.joysuch.wwyt.edu.service.EduCertificateNotifyService;
import com.joysuch.wwyt.edu.service.WwytEduCertificateService;
import com.joysuch.wwyt.edu.service.WwytEduCertificateUserService;
import lombok.extern.slf4j.Slf4j;
import net.sf.ehcache.util.NamedThreadFactory;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
@Slf4j
public class EduCertificateNotifySchedule {

    @Autowired
    private EduCertificateNotifyService eduCertificateNotifyService;
    @Autowired
    private WwytEduCertificateUserService wwytEduCertificateUserService;
    @Autowired
    private BaseManageTypeService baseManageTypeService;
    @Autowired
    private WwytEduCertificateService wwytEduCertificateService;

    @Autowired
    private BaseUserDao baseUserDao;
    @Autowired
    private BpDepartDao bpUDepartDao;
    @Autowired
    private CommonIndexNotifyDao commonIndexNotifyDao;
    @Autowired
    private BpContractorStaffDao bpContractorStaffDao;
    @Autowired
    private CommonBusinessNoticeService commonBusinessNoticeService;

    private static ExecutorService executor = new ThreadPoolExecutor(1, 4, 10, TimeUnit.MINUTES,
            new LinkedBlockingQueue<>(10000),
            new NamedThreadFactory("certificate-notify-expire-check-thread"),
            new ThreadPoolExecutor.CallerRunsPolicy());

    /**
     * 查找即将过期需要提醒的证书
     */
    @Scheduled(cron = "0 10 0 * * ?")   // 正式环境每天00:10跑一次
//    @Scheduled(cron = "0 0/2 * * * ? ")   // 开发、测试环境2分钟跑一次
//    @Scheduled(fixedDelay = 60000, initialDelay = 10000L)
    public void checkExpired() {
        //查找所有启用的证书提醒
        LambdaQueryWrapper<EduCertificateNotify> cnlqw = new LambdaQueryWrapper<>();
        cnlqw.eq(EduCertificateNotify::getStatus, CertificateNotifyStatus.KAI_QI.getId());
        cnlqw.isNotNull(EduCertificateNotify::getCertificates);
        List<EduCertificateNotify> certificateNotifyList = eduCertificateNotifyService.list(cnlqw);

        if (CollUtil.isNotEmpty(certificateNotifyList)) {
            //循环针对证书提醒设置进行查找
            for (EduCertificateNotify eduCertificateNotify : certificateNotifyList) {
                executor.submit(() -> {
                    checkOneExpired(eduCertificateNotify);
                });
            }
        }
    }

    /**
     * 根据某个证书提醒查询持证
     *
     * @param eduCertificateNotify
     */
    private void checkOneExpired(EduCertificateNotify eduCertificateNotify) {

        switch (CertificateNotifyType.findById(eduCertificateNotify.getType())) {
            case YOU_XIAO_QI:
                checkOneExpiredWithYouXiaoQi(eduCertificateNotify);
                break;
            case FU_ZHENG_DAO_QI:
                checkOneExpiredWithFuZhengDaoQi(eduCertificateNotify);
                break;
            default:
                log.warn("EduCertificateNotifySchedule error type:{}", JSON.toJSONString(eduCertificateNotify));
        }

    }

    /**
     * 根据证书提醒进行有效期到期提醒
     *
     * @param eduCertificateNotify
     */
    private void checkOneExpiredWithYouXiaoQi(EduCertificateNotify eduCertificateNotify) {

        List<Long> certficateIdList = tranferToCertIdList(eduCertificateNotify.getCertificates());
        if (CollUtil.isEmpty(certficateIdList)) {
            return;
        }

        //查询有效期结束时间-提醒提前天数等于当前日期的持证, 有效期结束时间等于当前日期+提前天数+1
        LambdaQueryWrapper<WwytEduCertificateUser> lqw = new LambdaQueryWrapper<>();
        lqw.eq(WwytEduCertificateUser::getTenentId, eduCertificateNotify.getTenentId());
        lqw.eq(WwytEduCertificateUser::getCertStatus,CertStatus.EFFECTIVE.status.toString());
        lqw.like(WwytEduCertificateUser::getOrgCode, eduCertificateNotify.getOrgCode());
        lqw.in(WwytEduCertificateUser::getCertId, certficateIdList);
        lqw.isNotNull(WwytEduCertificateUser::getOutOfDateTime);
        lqw.apply("date_format(OUT_OF_DATE_TIME,'%y%m%d') <= date_format({0},'%y%m%d') ", DateUtil.offsetDay(DateUtil.date(), eduCertificateNotify.getAheadDays() == null ? 1 : eduCertificateNotify.getAheadDays()));
        List<WwytEduCertificateUser> needNotifyList = wwytEduCertificateUserService.list(lqw);

        //提醒
        notify(eduCertificateNotify, needNotifyList , CommonIndexNotifyTypes.CERT_MATURITY);

        //更改证书的状态
        if (CollectionUtils.isNotEmpty(needNotifyList)) {
            for (WwytEduCertificateUser eduCertificateUser : needNotifyList) {
                eduCertificateUser.setCertStatus(CertStatus.EXPIRING_SOON.status.toString());
            }
            wwytEduCertificateUserService.updateBatchById(needNotifyList);
        }

    }

    /**
     * 根据证书提醒进行复证到期提醒
     *
     * @param eduCertificateNotify
     */
    private void checkOneExpiredWithFuZhengDaoQi(EduCertificateNotify eduCertificateNotify) {

        List<Long> certficateIdList = tranferToCertIdList(eduCertificateNotify.getCertificates());
        if (CollUtil.isEmpty(certficateIdList)) {
            return;
        }

        //查询复证时间-提醒提前天数等于当前日期的持证, 复证时间等于当前日期+提前天数
        LambdaQueryWrapper<WwytEduCertificateUser> lqw = new LambdaQueryWrapper<>();
        lqw.eq(WwytEduCertificateUser::getTenentId, eduCertificateNotify.getTenentId());
        lqw.like(WwytEduCertificateUser::getOrgCode, eduCertificateNotify.getOrgCode());
        lqw.in(WwytEduCertificateUser::getCertId, certficateIdList);
        lqw.isNotNull(WwytEduCertificateUser::getRepeatViewDateTime);
        lqw.apply("date_format(REPEAT_VIEW_DATE_TIME,'%y%m%d') <= date_format({0},'%y%m%d') ", DateUtil.offsetDay(DateUtil.date(), eduCertificateNotify.getAheadDays() == null ? 1 : eduCertificateNotify.getAheadDays()));
        List<WwytEduCertificateUser> needNotifyList = wwytEduCertificateUserService.list(lqw);

        //提醒
        notify(eduCertificateNotify, needNotifyList , CommonIndexNotifyTypes.CERT_RECERTIFICATION);
    }

    /**
     * 产生提醒
     *
     * @param certificateUserList
     */
    private void notify(EduCertificateNotify eduCertificateNotify, List<WwytEduCertificateUser> certificateUserList,CommonIndexNotifyTypes type) {
        if (CollUtil.isEmpty(certificateUserList)) {
            return;
        }

        for (WwytEduCertificateUser certificateUser : certificateUserList) {
            //获取当前持证人员 消息通知目前只提醒本公司人员
            BaseUser user = null;
            String peopleName = "";
            String peopleCode = "";
            if (certificateUser.getUserContractorFlag() != null && !certificateUser.getUserContractorFlag()) {
                Optional<BaseUser> userOptional = baseUserDao.findById(certificateUser.getUserId());
                if (!userOptional.isPresent()) {
                    continue;
                }
                user = userOptional.get();
                if (user != null) {
                    peopleName = user.getRealName();
                    peopleCode = user.getCode();
                }
            } else if (certificateUser.getUserContractorFlag() != null && certificateUser.getUserContractorFlag()) {
                Optional<BpContractorStaff> obs = bpContractorStaffDao.findById(certificateUser.getUserId());
                if (obs.isPresent()) {
                    peopleName = obs.get().getName();
                    peopleCode = obs.get().getCode();
                }
            }
            Set<BaseUser> userList = getNotifyUser(eduCertificateNotify, certificateUser, user);
            if (CollUtil.isNotEmpty(userList)) {
                saveNotify(eduCertificateNotify, certificateUser, userList, peopleName, peopleCode , type);
            }
        }

    }

    /**
     * 获取  提醒人员列表
     *
     * @param eduCertificateNotify
     * @param certificateUser
     * @return
     */
    private Set<BaseUser> getNotifyUser(EduCertificateNotify eduCertificateNotify, WwytEduCertificateUser certificateUser, BaseUser user) {

        Set<BaseUser> resultList = Sets.newHashSet();


        if (eduCertificateNotify.getNotifySelfFlag() && user != null) {
            resultList.add(user);
        }

        switch (CertificateNotifyObjType.findById(eduCertificateNotify.getObjType())) {
            case REN_YUAN:
                if (StringUtils.isNotBlank(eduCertificateNotify.getObjId())) {
                    String[] split = eduCertificateNotify.getObjId().split(",");
                    List<Long> userIds = Arrays.stream(split)
                            .map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());
                    List<BaseUser> notifyUsers = baseUserDao.findAllById(userIds);
                    if (CollUtil.isNotEmpty(notifyUsers)) {
                        resultList.addAll(notifyUsers);
                    }
                }
                break;
            case GUAN_LI_LEI_XING:
                if (StringUtils.isNotBlank(eduCertificateNotify.getObjId()) && user != null) {
                    //证书提醒里会存储该管理类型数据字典id，需要根据该字典id以及证书拥有者部门信息去查出具体管理类型的人员
                    LambdaQueryWrapper<BaseManageType> lqw = new LambdaQueryWrapper<>();
                    lqw.eq(BaseManageType::getManageTypeDictionaryCode, Long.valueOf(eduCertificateNotify.getObjId()));
                    lqw.eq(BaseManageType::getTenentId, eduCertificateNotify.getTenentId());
                    lqw.like(BaseManageType::getOrgCode, eduCertificateNotify.getOrgCode());
                    lqw.eq(BaseManageType::getDepartId, user.getDepartId());
                    List<BaseManageType> manageTypeList = baseManageTypeService.list(lqw);
                    if (CollUtil.isNotEmpty(manageTypeList)) {
                        List<Long> manageTypeUserIds = manageTypeList.parallelStream().map(t -> t.getUserId()).collect(Collectors.toList());
                        List<BaseUser> notifyUsers = baseUserDao.findAllById(manageTypeUserIds);
                        if (CollUtil.isNotEmpty(notifyUsers)) {
                            resultList.addAll(notifyUsers);
                        }
                    }
                }
                break;
            case BU_MEN_FU_ZE_REN:
                if (user != null) {
                    //查询部门
                    Optional<BpDepart> departOptional = bpUDepartDao.findById(user.getDepartId());
                    if (!departOptional.isPresent()) {
                        break;
                    }
                    BpDepart depart = departOptional.get();
                    if (null != depart.getChiefId()) {
                        //查询部门负责人
                        Optional<BaseUser> notifyUserOptional = baseUserDao.findById(depart.getChiefId());
                        if (notifyUserOptional.isPresent()) {
                            resultList.add(notifyUserOptional.get());
                        }
                    }
                }
                break;
            default:
                break;
        }

        return resultList;
    }

    private void saveNotify(EduCertificateNotify eduCertificateNotify, WwytEduCertificateUser certificateUser, Set<BaseUser> userList, String peopleName, String peopleCode ,CommonIndexNotifyTypes type) {
        List<CommonIndexNotify> notifyList = Lists.newArrayList();
        List<CommonBusinessNotice> commonBusinessNoticeList = Lists.newArrayList();
        for (BaseUser baseUser : userList) {
            //提醒逻辑更改为证书提醒时间加上提前量后的提醒时间在当前时间之前的需要发送提醒消息,
            //为了防止重复生成提醒消息,以cert_user_id,和提醒时间以及提醒人员id,以及证书管理的类型(到期，复证),以及证书的过期日期为条件去查重,符合条件的不去生成通知
            CommonIndexNotify notify = new CommonIndexNotify();
            CommonBusinessNotice commonBusinessNotice = new CommonBusinessNotice();
            StringBuilder msgSB = new StringBuilder();
            //因为是定时任务生成的通知，兼容提前量大于剩余时间的数据，提醒时间统一取当前时间
            switch (CertificateNotifyType.findById(eduCertificateNotify.getType())) {
                case YOU_XIAO_QI:
                    DateTime dateTime = DateUtil.offsetDay(certificateUser.getOutOfDateTime(), -eduCertificateNotify.getAheadDays());
                    if ( dateTime.isBefore(new Date()) && notNeedNotify(dateTime, certificateUser.getOutOfDateTime(), certificateUser.getId(), baseUser.getId(), type.getType())){
                        continue;
                    }
                    notify.setBusinessDate(certificateUser.getOutOfDateTime());
                    notify.setRemindDate(dateTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
                    notify.setRemindDateTime(dateTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().atTime(0, 1));
                    msgSB.append("证书到期提醒,姓名:");
                    msgSB.append(StrUtil.isNotEmpty(peopleName) ? peopleName : "未知");
                    msgSB.append(",员工编号:");
                    msgSB.append(StrUtil.isNotEmpty(peopleCode) ? peopleCode : "未知");
                    msgSB.append(",所持有");
                    WwytEduCertificate certificate = wwytEduCertificateService.getById(certificateUser.getCertId());
                    if (certificate != null) {
                        msgSB.append(certificate.getName());
                    } else {
                        msgSB.append("证书");
                    }
                    msgSB.append("将于");
                    msgSB.append(DateUtil.format(certificateUser.getOutOfDateTime(), "yyyy-MM-dd"));
                    msgSB.append("日到期,证书编号:");
                    msgSB.append(certificateUser.getCertNo());
                    commonBusinessNotice.setBusinessType(AppBusinessNoticeTypes.CERT_MATURITY.getType());
                    commonBusinessNotice.setRefId(certificate == null ? null : certificate.getId());
                    commonBusinessNotice.setNoticeTitle((certificate == null ? "" : certificate.getName()) + "到期提醒");
                    break;
                case FU_ZHENG_DAO_QI:
                    DateTime dateTime1 = DateUtil.offsetDay(certificateUser.getRepeatViewDateTime(), -eduCertificateNotify.getAheadDays());
                    if (dateTime1.isBefore(new Date()) && notNeedNotify(dateTime1, certificateUser.getRepeatViewDateTime(), certificateUser.getId(), baseUser.getId(), type.getType())){
                        continue;
                    }
                    notify.setBusinessDate(certificateUser.getRepeatViewDateTime());
                    notify.setRemindDate(dateTime1.toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
                    notify.setRemindDateTime(dateTime1.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().atTime(0, 1));
                    msgSB.append("复证到期提醒,姓名:");
                    msgSB.append(StrUtil.isNotEmpty(peopleName) ? peopleName : "未知");
                    msgSB.append(",员工编号:");
                    msgSB.append(StrUtil.isNotEmpty(peopleCode) ? peopleCode : "未知");
                    msgSB.append(",所持有");
                    WwytEduCertificate certificate1 = wwytEduCertificateService.getById(certificateUser.getCertId());
                    if (certificate1 != null) {
                        msgSB.append(certificate1.getName());
                    } else {
                        msgSB.append("证书");
                    }
                    msgSB.append("将于");
                    msgSB.append(DateUtil.format(certificateUser.getRepeatViewDateTime(), "yyyy-MM-dd"));
                    msgSB.append("日进行复证,证书编号:");
                    msgSB.append(certificateUser.getCertNo());
                    commonBusinessNotice.setBusinessType(AppBusinessNoticeTypes.CERT_RECERTIFICATION.getType());
                    commonBusinessNotice.setRefId(certificate1 == null ? null : certificate1.getId());
                    commonBusinessNotice.setNoticeTitle((certificate1 == null ? "" : certificate1.getName()) + "复证提醒");
                    break;
                default:
                    log.warn("EduCertificateNotifySchedule error type:{}", JSON.toJSONString(eduCertificateNotify));
            }
            commonBusinessNotice.setNoticeUser(baseUser.getId());
            commonBusinessNotice.setNoticeContent(msgSB.toString());
            commonBusinessNotice.setReaded("0");
            commonBusinessNotice.setSendTime(new Date());
            commonBusinessNotice.setOrgCode(eduCertificateNotify.getOrgCode());
            commonBusinessNotice.setTenentId(eduCertificateNotify.getTenentId());
            commonBusinessNoticeList.add(commonBusinessNotice);
            notify.setType(type.getType());
            notify.setTenentId(eduCertificateNotify.getTenentId());
            notify.setOrgCode(eduCertificateNotify.getOrgCode());
            notify.setCreateBy(null);
            notify.setUpdateBy(null);
            notify.setReadFlag(0);
            notify.setQueueOrNot(1);
            notify.setRemindedOrNot(1);
            notify.setEduCertUserId(certificateUser.getUserId());
            notify.setUserId(baseUser.getId());
            notify.setRecordId(certificateUser.getId());
            notify.setNotifyMessage(msgSB.toString());
            notifyList.add(notify);
        }
        commonIndexNotifyDao.saveAll(notifyList);
        if (CollectionUtils.isNotEmpty(commonBusinessNoticeList)) {
            commonBusinessNoticeService.saveBatch(commonBusinessNoticeList);
        }
    }


    private List<Long> tranferToCertIdList(String ids) {
        List<String> certficateIdStrList = StrUtil.split(ids, ',');
        if (CollUtil.isEmpty(certficateIdStrList)) {
            return null;
        }
        return certficateIdStrList.parallelStream().map(Long::parseLong).collect(Collectors.toList());
    }

    private boolean notNeedNotify(DateTime remindDate,Date businessDate,Long certUserId,Long remindUserId,String type){
        Long count = commonIndexNotifyDao.needNotify(remindDate.toJdkDate() , businessDate , certUserId, remindUserId,type);
        return count != 0;
    }
}
